package com.mrjoew.weekend.configurations;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mrjoew.weekend.constants.HttpConstant;
import com.mrjoew.weekend.constants.SecurityConstant;
import com.mrjoew.weekend.services.sys.SysAccountService;
import com.mrjoew.weekend.services.sys.SysRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author Christopher Jones
 * @ClassName SecurityConfig
 * @explain
 * @date 2020/6/17 0017 13:36
 */
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final SysAccountService sysAccountService;

    private final SysRoleService sysRoleService;

    @Autowired
    public SecurityConfiguration(SysAccountService sysAccountService, SysRoleService sysRoleService) {
        this.sysAccountService = sysAccountService;
        this.sysRoleService = sysRoleService;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return sysAccountService::loadUserByUsername;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors();
        http.csrf().disable();
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                // 表单登录 - 允许访问 .successHandler(new AuthenticationSuccessHandler()).failureHandler(new AuthenticationFailureHandler())
                .and().formLogin().permitAll()
                .and().logout().permitAll().invalidateHttpSession(true)
                .and().requestMatchers().anyRequest()

                .and().authorizeRequests()
                // 获取所有匿名访问
                .antMatchers(sysRoleService.anonymous()).permitAll()
                .anyRequest().authenticated();
    }
}


@Slf4j
@Component
class AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        log.error(SecurityConstant.LOGIN_FAILURE);

        response.setContentType(HttpConstant.ContentType.APPLICATION_JSON);
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.getWriter().write(new ObjectMapper().writeValueAsString(exception.getMessage()));
    }
}

@Component
class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        response.setContentType(HttpConstant.ContentType.APPLICATION_JSON);
        response.getWriter().write(new ObjectMapper().writeValueAsString(authentication));
    }
}